// https://bj.sydc.anjuke.com/sp-shou/?pi=baidu-cpcjp-bj-sp-tyong1&kwid=141073555411

const puppeteer = require("puppeteer");

const fs = require("fs");

const baseURL =
  "https://bj.sydc.anjuke.com/sp-shou/?pi=baidu-cpcjp-bj-sp-tyong1&kwid=141073555411";

// 不用立即函数了，封装为 模块
// 初始化puppeteer
async function initPuppeteer() {
  const browser = await puppeteer.launch({ headless: false });

  const page = await browser.newPage();

  await page.goto(baseURL);

  //   绑定外部方法
  page.exposeFunction("getPhone", getPhone);

  // 进阶实现参数爬取（无非是在爬取数据之前，先点击几个按钮）
  const params = {
    pos: "朝阳", // 位置
    area: "", // 面积
    price: "", // 价格
  };

  //   首页也会有机器校验问题
  if (!(await page.waitForSelector('div[class="list-left"]')))
    await page.click('input[class="btn_tj"]');

  console.log("## 开始获取店铺信息--");

  //   可以使用选择器实现，也可以直接在上下文执行 的 page.evaluate(pageFunction[, ...args])
  const data = await page.$eval(
    'div[class="list-left"]',
    (listLeft, params) => {
      // 这里不用document,而是在 已经选中的基础上进行下一步操作
      const items = listLeft.querySelectorAll('div[class="list-item"]');

      let dataMap = [];

      items.forEach(async (item, index) => {
        if (index > 20) return; // 这里控制了整体爬取的数量，不然就是整体的60多条
        // 这里获取的是每一项数据，可以直接拿到信息
        const item_a_link = item.querySelector("a");

        //   获取图片链接
        const item_img_src = item_a_link
          .querySelector('div[class="item-img"]')
          .querySelector("img")
          .getAttribute("src");

        // 获取标题
        const item_title = item_a_link
          .querySelector('div[class="item-info"]')
          .querySelector('div[class="item-title"]')
          .querySelector("span").innerText;

        // 获取 联系人 名称
        const item_user = item_a_link
          .querySelector('div[class="item-info"]')
          .querySelectorAll("p")[2]
          .querySelector("span").innerText;

        // 获取联系方式
        const item_user_phone_url = item_a_link.getAttribute("href");

        //   返回数据
        dataMap.push({
          img: item_img_src,
          title: item_title,
          user: item_user,
          phoneUrl: item_user_phone_url,
        });
      });

      return dataMap;
    },
    params
  );

  /**
   * 备注：这里的思路错了，不能在内部浏览器中进行点击，
   * 内部控制台没有权限等待页面点击刷新，需要在内部返回映射，
   * 在外部使用 page.click()实现真正的点击，
   * 然后再执行page.$eval() 执行我们上面的逻辑，我写到 boss 才想起来，就不重写了。不懂的可以再联系我
   */

  // 【为何不用 page.$eval()?当我们点击按钮的时候，这个列表是变化的，直接使用 page.evaluate()】
  // const data = await page.evaluate(async (params) => {
  //   console.clear();
  //   let dataMap = [];
  //   /**
  //    * 为了确保 点击的精准性，生成实时映射
  //    */

  //   if (params.pos) {
  //     // 位置映射
  //     let posList = [];
  //     // 拿到所有链接
  //     const pos_a_links = document
  //       .querySelector("div#district-item")
  //       .querySelectorAll("a");

  //     pos_a_links.forEach((i) => posList.push(i.innerText));

  //     // 拿到入参，对比该点击的按钮
  //     pos_a_links[posList.findIndex((i) => i === params.pos)]?.click();
  //   }
  //   if (params.area) {
  //     // 其他条件类似，我就不演示了
  //   }
  //   if (params.price) {
  //     // 其他条件类似，我就不演示了
  //   }
  //   // 然后老样子，获取左侧列表，返回数据，请求phone

  //   const items = document
  //     .querySelector('div[class="list-left"')
  //     .querySelectorAll('div[class="list-item"]');
  //   items.forEach(async (item) => {
  //     // 这里获取的是每一项数据，可以直接拿到信息
  //     const item_a_link = item.querySelector("a");

  //     //   获取图片链接
  //     const item_img_src = item_a_link
  //       .querySelector('div[class="item-img"]')
  //       .querySelector("img")
  //       .getAttribute("src");

  //     // 获取标题
  //     const item_title = item_a_link
  //       .querySelector('div[class="item-info"]')
  //       .querySelector('div[class="item-title"]')
  //       .querySelector("span").innerText;

  //     // 获取 联系人 名称
  //     const item_user = item_a_link
  //       .querySelector('div[class="item-info"]')
  //       .querySelectorAll("p")[2]
  //       .querySelector("span").innerText;

  //     // 获取联系方式
  //     const item_user_phone_url = item_a_link.getAttribute("href");

  //     //   返回数据
  //     dataMap.push({
  //       img: item_img_src,
  //       title: item_title,
  //       user: item_user,
  //       phoneUrl: item_user_phone_url,
  //     });
  //   });

  //   return dataMap;
  // }, params);

  for (const item of data) {
    if (!item.phoneUrl) return;
    // 请求phone
    await page.goto(item.phoneUrl);

    // 处理 元素不存在，需要点击校验的问题(存在机器校验问题，需要等待元素)

    if (!(await page.waitForSelector('div[class="tel-wrap"]')))
      await page.click('input[class="btn_tj"]');

    await page.waitForSelector('div[class="tel-wrap"]');
    //   点击 电话联系ta 显示号码
    await page.click('div[class="tel-wrap"]');

    //   等待元素
    await page.waitForSelector('div[class="tel-phone-number"]');

    //   获取号码
    const phone = await page.evaluate(() => {
      return document.querySelector('div[class="tel-phone-number"]').innerText;
    });

    console.log("## 获取用户联系方式成功：", phone);

    item.phone = phone;

    // 可以考虑删除 phone_url 属性了
    delete item.phoneUrl;

    // 这里不要 page.close() 不然没有操作页面，其二 close 后，一定要 newPage(),两种方案
  }

  console.log("## 店铺信息获取完成，开始转存文件");

  // 存文件
  saveFile(data);

  await browser.close();
}

// 联系方式需要跳转新的页面，因此绑定方法传入内部【此方式会导致页面崩溃，不可使用，使用异步实现】
async function getPhone(url) {
  // 这个就不用开启浏览器了，一定记得关闭哈！！！
  const phone_browser = await puppeteer.launch({ headless: true });

  const phone_page = await phone_browser.newPage();

  await phone_page.goto(url);

  //   点击 电话联系ta 显示号码
  await phone_page.click('div[class="tel-wrap"]');

  //   等待元素
  await phone_page.waitForSelector('div[class="tel-phone-number"]');
  //   获取号码
  const phone = await phone_page.evaluate(() => {
    return document.querySelector('div[class="tel-phone-number"]').innerText;
  });
  //   这个页面一定记得关闭，不然会出现 很多页面
  console.log("phone", phone);

  await phone_browser.close();

  return phone;
}

// 转存为文件
function saveFile(data) {
  fs.writeFile("./demo/file/店铺信息.json", JSON.stringify(data), (err) => {
    if (err) return console.log("err\t", err);
    console.log("文件写入成功");
  });
}

initPuppeteer();
